<?php
/*
 * Author <emilio.astarita@gmail.com>
 * repo: http://code.google.com/p/repl-php-ajax/
 */
if (!in_array(@$_SERVER['REMOTE_ADDR'], array('127.0.0.1', '::1'))){ die('Add your ip first.'); }

if(isset($_POST['data'])) {
  set_time_limit(5);
  error_reporting(E_ALL);
  ini_set('display_errors','1');
  /*
    fsx dot nr01 at gmail dot com
    13-Jun-2008 12:22
    http://ar2.php.net/manual/en/function.highlight-string.php#83824
   */
  function ____printCode($source_code) {
    if (is_array($source_code))
      return false;
    $source_code = explode("\n", str_replace(array("\r\n", "\r"), "\n", $source_code));
    $line_count = 1;
    $formatted_code = '';
    foreach ($source_code as $code_line) {
      $formatted_code .= '<tr><td>'.$line_count.'</td>';
      $line_count++;
      if (@ereg('<\?(php)?[^[:graph:]]', $code_line))
        $formatted_code .= '<td>'. str_replace(array('<code>', '</code>'), '', highlight_string($code_line, true)).'</td></tr>';
      else
        $formatted_code .= '<td>'.@ereg_replace('(&lt;\?php&nbsp;)+', '', str_replace(array('<code>', '</code>'), '', highlight_string('<?php '.$code_line, true))).'</td></tr>';
    }
    return '<table style="font: 1em Consolas, \'andale mono\', \'monotype.com\', \'lucida console\', monospace;">'.$formatted_code.'</table>';
  }
  /*
    nicolas dot grekas+php at gmail dot com
    20-Oct-2008 08:20
    http://www.php.net/manual/en/function.php-check-syntax.php#86466    
   */
  function php_syntax_error($code) {
    $code = "\n" . $code;
    $braces = 0;
    $inString = 0;
    foreach (token_get_all('<?php ' . $code) as $token) {
      if (is_array($token)) {
        switch ($token[0]) {
        case T_CURLY_OPEN:
        case T_DOLLAR_OPEN_CURLY_BRACES:
        case T_START_HEREDOC: ++$inString; break;
        case T_END_HEREDOC:   --$inString; break;
        }
      } else if ($inString & 1) {
            switch ($token) {
              case '`':
              case '"': --$inString; break;
              }
          } else {
            switch ($token) {
              case '`':
              case '"': ++$inString; break;
              case '{': ++$braces; break;
              case '}':
                if ($inString) --$inString;
                else {
                  --$braces;
                  if ($braces < 0) break 2;
                }
                break;
            }
      }
    }
    $inString = @ini_set('log_errors', false);
    $token = @ini_set('display_errors', true);
    ob_start();
    $braces || $code = "if(0){{$code}\n}";
    if (false === eval($code)) {
      if ($braces) $braces = PHP_INT_MAX;
      else {
        false !== strpos($code, "\r") && $code = strtr(str_replace("\r\n", "\n", $code), "\r", "\n");
        $braces = substr_count($code, "\n");
      }
      $code = ob_get_clean();
      $code = strip_tags($code);
      if (preg_match("'syntax error, (.+) in .+ on line (\d+)$'s", $code, $code)) {
        $code[2] = (int) $code[2];
        $code = $code[2] <= $braces
          ? array($code[1], $code[2])
          : array('unexpected $end' . substr($code[1], 14), $braces);
      } else
        $code = array('syntax error', 0);
    } else {
      ob_end_clean();
      $code = false;
    }
    @ini_set('display_errors', $token);
    @ini_set('log_errors', $inString);
    return $code;
  }

  function ____showSyntaxErrors($toEval) {
    $errors = php_syntax_error($toEval);
    if(is_array($errors)) {
      echo '<p class="phpError"><span class="lineError">Line: '. $errors[1] . ' </span> ' . $errors[0] . '</p>';
      die();
    }
  }

  if(get_magic_quotes_gpc()) {
    $_POST = array_map('stripslashes',$_POST);
    $_GET =  array_map('stripslashes',$_GET);
  }
  $data = json_decode($_POST['data'],true);
  $toEval = $data['toEval'];
  $options = $data['options'];
  function ____raw($toEval) {
    echo ____printCode("<?php \n"  . $toEval . "\n ?>");
    ____showSyntaxErrors($toEval);
    ob_start();
    eval($toEval);
    $data = ob_get_contents();
    ob_end_clean();
    die('<pre>'.htmlentities($data).'</pre>');
  }
  function ____render($toEval) {
    echo ____printCode("<?php \n"  . $toEval . "\n ?>");
    ____showSyntaxErrors($toEval);
    ob_start();
    eval($toEval);
    $data = ob_get_contents();
    ob_end_clean();
    die('<div>'.$data .'</div>');
  }
  call_user_func('____' . $options['render'],$toEval);
  die();
}
?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml">
  <head> 
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 
    <title></title> 
    <style type="text/css">
      body {
         font-family: Arial, Helvetica, sans-serif;
      }
      #tip {
         padding: 5px;
         text-align: center;
         color: #00529B;
         background-color: #BDE5F8;
         border: 1px solid #000;
      }
      #warning {
         padding: 5px;
         text-align: center;
         color: #D8000C;
         background-color: #FFBABA;
         border: 1px solid #000;
      }
      #options {
         height: 28px;
         background: #E6E6E6;
         color: #000;
         margin: 0px;padding: 0px;
         border: 2px solid #000;
         text-shadow: 1px 1px 1px #ddd;
         -moz-border-radius: 5px; -webkit-border-radius: 5px;
      }
      #options span {
         font-size: 10px;
         line-height: 24px;
         margin: 0px;
         padding:4px;
         display: block;
         float: left;
         height: 20px;
         cursor: pointer;
         width: 80px;
         border-right: 1px solid #000;
         text-align: center;
      }
      span.notPressed {
         background: #DDDDDD;
      }
      span.pressed {
         font-weight: bold;
         background: #D1D1D1;
         text-decoration: underline;
      }
      #options span:hover {
         background: #FFF;
      }
      #outputContainer {
         height: 200px;
         overflow: auto;
         position:relative;
      }
      #output {
          /*position:absolute;*/
          bottom:0;
          font-family: monospace, Courier;
          font-size: 11px;
          width: 100%;
      }
      #output p {
         border: solid 1px #F3F3F3;
         margin: 0;
         padding: 2px;
         min-height: 20px;
      }
      #output p.userInput {
         border: solid 1px #F3F3F3;
         font-family: Courier;
         margin: 0;
         padding: 2px;
         padding-left: 10px;
         color: #6196EE;
         min-height: 20px;
         background:url() left no-repeat ;
      }      
      #inputData{
         font-family: Courier;
         height: 20px;
         width: 100%;
         line-height: 20px;
         resize: none;
         background:url() left no-repeat ;
         padding-left: 10px;
         border: none;
         border-top: solid #DDD 1px;
         border-bottom: solid #DDD 1px;
      }
      #inputData:hover{
         border-top: solid #000 1px;
         border-bottom: solid #000 1px;
      }
      #inputData:focus{
         outline: none;
         border: none;
         border-top: solid #000 1px;
         border-bottom: solid #000 1px;
      }
      .footer {
         margin-top: 30px;
         font-size: 10px;
         text-align: center;
      }
    </style>
    <script type="text/javascript">
      (function (options) {
          defaultOptions = {debug: true, render: 'raw'};
          for (var attrName in defaultOptions) { 
              if(defaultOptions.hasOwnProperty(attrName) && typeof(options[attrName]) == "undefined")
                  options[attrName] = defaultOptions[attrName];
          }
          
          var log = function(x) {
              if((typeof(window['console']) == "undefined") || !options.debug)
                  return;
              return window.console.log(x);
          }

          var dir = function(x) {
              if((typeof(window['console']) == "undefined"))
                  return;
              return window.console.dir(x);
          }
          dir(options);
          var id = function(x){return document.getElementById(x);};
          var html = function(e,html) {
              if(typeof(e) == "string")
                  e = id(e);
              if(typeof(html) == "undefined")
                  return e.innerHTML;
              return e.innerHTML = html;
          };
          var _xhr = function() {
              if (window.XMLHttpRequest) {
                  return new XMLHttpRequest();
              } else if (window.ActiveXObject) {
                  return new ActiveXObject('Microsoft.XMLHTTP')
              } else {
                  log("Browser unsupported.");
                  return null;
              }
          };
          var xhr = _xhr();
          var history = [];
          var historyPos = null;
          var sendDataToServer = function(data) {
              xhr.open("post", "<?php echo $_SERVER['PHP_SELF']; ?>", true);
              xhr.onreadystatechange = function() {
                if (xhr.readyState != 4) { return; }
                var serverResponse = xhr.responseText;
                log('Server response: '+ serverResponse);
                id('inputData').value = "";
                var responseInDom = document.createElement('p');
                responseInDom.innerHTML = serverResponse;
                id('output').appendChild(responseInDom);
                id('outputContainer').scrollTop = id('outputContainer').scrollHeight;
              };
              xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
              var toSend = {toEval: data, options: options };
              log('sending data to de server:');
              dir(toSend);
              xhr.send("data="+ encodeURIComponent(JSON.stringify(toSend)).replace(/%20/g, "+") );
          }
          var send = function() {
              log('called send');
              var value = id('inputData').value;
              if(value.length)
                history.push(value);
              sendDataToServer(value);
              //dir(history);
          }
        var changeInput = function() {
          id('inputData').style.height = (20 * linesOfInput()) + 'px';
        }
        var linesOfInput = function() {
          var ln = id('inputData').value.split("\n").length;
          return ln;
        }
        var navigateHistory = function() {
          return (linesOfInput() == 1) && history.length;
        }
          var keydownInput = function(ev) {
            //dir(ev);
            if(ev.keyCode == 13 && ev.shiftKey) { // shift + return
              return;
            }
            if(ev.keyCode == 13) { // return
              send();
              historyPos = history.length;
            }
            if(ev.keyCode == 38) { // up arrow
              if(!navigateHistory())
                return;
              if(!historyPos)
                return;
              historyPos--;
              id('inputData').value = history[historyPos];
            }
            if(ev.keyCode == 40) { // down arrow
              if(!navigateHistory())
                return;

              if(historyPos == (history.length -1))
                return;
              historyPos++;              
              id('inputData').value = history[historyPos];
            }
          }
          var rawOutput = function() {
              options.render = 'raw';
              id('rawOutput').className = 'pressed';
              id('renderOutput').className = 'notPressed';
          };
          var renderOutput = function() {
              options.render = 'render';
              id('renderOutput').className = 'pressed';
              id('rawOutput').className = 'notPressed';
          };
          var cleanOutput = function() {
              id('output').innerHTML = '';
          };

          var loadDocument = function() {
            log('loadDocument')
            id('inputData').addEventListener('keydown',keydownInput,false);
            id('inputData').addEventListener('keyup',changeInput,false);
            id('inputData').addEventListener('change',changeInput,false);
            id('rawOutput').addEventListener('click',rawOutput,false);
            id('renderOutput').addEventListener('click',renderOutput,false);
            id('cleanOutput').addEventListener('click',cleanOutput,false);
            id('inputData').focus();
          };
          window.addEventListener('load',loadDocument,false);
      })({debug:false});
    </script>
  </head> 
  <body> 
    <div id="options">
      <span id="rawOutput" class="pressed">RAW</span>
      <span id="renderOutput"  class="notPressed">RENDER</span>
      <span id="cleanOutput" class="notPressed">CLEAN</span>
    </div>
    <div id="outputContainer">
      <div id="output">
        <p>Try type something, ie: echo phpversion();</p>
      </div>
    </div>
    <textarea id="inputData" wrap="off"  rows="4" cols="115"></textarea>
    <p id="warning"><strong>WARNING</strong>: This console is
    extremely dangereous in a production server. Use only in your
    localhost.</p>
    <p id="tip"><strong>TIP</strong>: You can use <strong>Up
    arrow</strong> and <strong>Down arrow</strong> to navigate the
    history of inputs. Use <strong>Shift + Enter</strong> to insert
    new lines.</p>
    <div class="footer">
      <p><acronym title="Repl Php Ajax"><a href="http://code.google.com/p/repl-php-ajax/">RPA</a></acronym></p>
      <p>Version 0.000</p>
      <p>Emilio Astarita &lt;emilio.astarita@gmail.com&gt;</p>
    </div>
  </body> 
</html> 
